﻿package game.test;

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Date;
import java.util.Timer;

class gameField extends Thread
{
    int[][] cnt; // Массив расчёта во время игры количества соседей.
    boolean pause;
    public boolean[][] cellsState; // Объекты на поле. Действительны до
                                   // окончания расчёта
    public boolean[][] cells; // Объекты на поле
    public boolean[][] cellsPrev; // Состояние поля на предыдущем шаге.
    public boolean[][] cellsPrevPrev; // Состояние поля на два шага назад.
                                      // Нужно, чтобы определить зацикленность.
    public int fieldHeight, fieldWidth;

    public void setCell(int _x, int _y)
    {
        synchronized (cells)
        {
            cells[_y][_x] = !cells[_y][_x];
            cellsState[_y][_x] = cells[_y][_x];
            System.out
                    .println("_x=" + _x + ", _y=" + _y + "=" + cellsState[_y][_x]);
        }
    }

    // Рассчитать новое состояние поля:
    public void counter()
    {
        synchronized (cells)
        {
            int i = 0, j = 0;
            int vert = cells.length, hor = cells[0].length;
            // Посчитать количество соседей:
            for (i = 0; i <= vert - 1; i++)
            {
                for (j = 0; j <= hor - 1; j++)
                {
                    cnt[i][j] = 0;
                    cnt[i][j] += cells[(i + vert - 1) % vert][(j + hor - 1) % hor] == true ? 1 : 0;
                    cnt[i][j] += cells[(i + vert - 1) % vert][(j + hor - 0) % hor] == true ? 1 : 0;
                    cnt[i][j] += cells[(i + vert - 1) % vert][(j + hor + 1) % hor] == true ? 1 : 0;
                    cnt[i][j] += cells[(i + vert - 0) % vert][(j + hor - 1) % hor] == true ? 1 : 0;
                    cnt[i][j] += cells[(i + vert - 0) % vert][(j + hor + 1) % hor] == true ? 1 : 0;
                    cnt[i][j] += cells[(i + vert + 1) % vert][(j + hor - 1) % hor] == true ? 1 : 0;
                    cnt[i][j] += cells[(i + vert + 1) % vert][(j + hor - 0) % hor] == true ? 1 : 0;
                    cnt[i][j] += cells[(i + vert + 1) % vert][(j + hor + 1) % hor] == true ? 1 : 0;
                }
            }
            // Определить новое состояние поля:
            for (i = 0; i <= vert - 1; i++)
            {
                for (j = 0; j <= hor - 1; j++)
                {
                    cellsPrevPrev[i][j] = cellsPrev[i][j];
                    cellsPrev[i][j] = cells[i][j];
                    if (cnt[i][j] < 2)
                    {
                        cells[i][j] = false;
                    }

                    if (cnt[i][j] == 3)
                    {
                        cells[i][j] = true;
                    }

                    if (cnt[i][j] > 3)
                    {
                        cells[i][j] = false;
                    }
                    if (cellsPrevPrev[i][j] != cells[i][j])
                    {
                    }
                }
            }
            for (i = 0; i <= vert - 1; i++)
            {
                for (j = 0; j <= hor - 1; j++)
                {
                    cellsState[i][j] = cells[i][j];
                }
            }
        }
    }

    public void run()
    {
        System.out.println("Go run...");
        while (true)
        {
            if (pause)
            {
                try
                {
                    Thread.sleep(100);
                }
                catch (InterruptedException e)
                {
                    System.out.println("Exception sleep in " + this.getName());
                    e.printStackTrace();
                }
            }
            else
            {
                counter();
            }
        }
    }

    gameField(int _x, int _y)
    {
        fieldHeight = _y;
        fieldWidth = _x;
        cellsState = new boolean[_y][_x];
        cells = new boolean[_y][_x];
        cellsPrev = new boolean[_y][_x];
        cellsPrevPrev = new boolean[_y][_x];
        cnt = new int[_y][_x];
    }

    public void clear()
    {
        int i = 0, j = 0;
        for (i = 0; i <= cells.length - 1; i++)
        {
            for (j = 0; j <= cells[i].length - 1; j++)
            {
                cells[i][j] = false;
            }
        }
    }

    // Определить текущее состояние поля
    public void getField(boolean[][] _cellsState)
    {
        int i = 0, j = 0;
        synchronized (cellsState)
        {
            for (i = 0; i <= cells.length - 1; i++)
            {
                for (j = 0; j <= cells[i].length - 1; j++)
                {
                    _cellsState[i][j] = cellsState[i][j];
                }
            }
        }
    }
}

/**
 * Класс игрового поля.
 * 
 * @author alex
 * 
 */

class classPlayField extends Canvas
{
    Dimension size;
    gameField gf;
    public boolean[][] cells; // Копия клеток для отображения на экране.
    int cellSize = 10; // Размер ячейки в px;
    Timer timer;
    boolean pause = false;
    int intCurrentPause, intUserPause, intPauseSlow = 500; // pause - задержка,
                                                           // заданная
                                                           // пользователем.
                                                           // pauseSlow -
                                                           // задержка,
                                                           // вызванная
                                                           // зацикленностью.

    Image buffer;
    int bufferX = 0, bufferY = 0;

    /** очистить содержимое и изображение игрового поля */
    public void clear()
    {
        gf.clear();
        paint(getGraphics());
    }

    // В конструкторе изначально задаётся размер игрового поля.
    public classPlayField(int _x, int _y)
    {
        size = new Dimension(_x, _y);
        this.setSize(300, 300);
        this.setVisible(true);
        gf = new gameField(_x, _y);
        cells = new boolean[_x][_y];

        this.addMouseListener(new MouseAdapter()
        {
            public void mousePressed(MouseEvent e)
            {
                // Определить координаты ячейки, куда нажал мышкой пользователь:
                int _x = (e.getX() + 1) / cellSize;
                int _y = (e.getY() + 1) / cellSize;
                if (_x > gf.fieldWidth - 1)
                {
                    _x = -1;
                }
                if (_y > gf.fieldHeight - 1)
                {
                    _y = -1;
                }
                if (_x >= 0 || _y >= 0)
                {
                    System.out.println("_x=" + _x + ", _y=" + _y);
                    // System.out.println("cells[y][x]="+cells[_y][_x]);

                    gf.setCell(_x, _y);
                    paint(getGraphics());
                }
                else
                {
                    System.out.println("Mouse out of field");
                }
            }
        });

        // Здесь можно переопределить некоторые события, которые происходят с
        // окном.
        // Добавим реакцию на изменение размеров окна. Посколько клетки должны
        // быть
        // всегда квадратными, то при изменении размеров окна изменение размеров
        // игрового поля должно происходит скачкообразно так
        // чтобы все клетки выглядели квадратами.
        this.addComponentListener(new ComponentAdapter()
        {
            public void componentResized(ComponentEvent e)
            {
                System.out
                        .println("resize: " + e.getComponent().getWidth() + "x" + e
                                .getComponent().getHeight());
                createBuffer();
            }
        });

    }

    /**
     * Создать заново буфер отрисовки изображения игрового поля
     */
    void createBuffer()
    {
        bufferX = getWidth();
        bufferY = getHeight();
        buffer = createImage(bufferX, bufferY);
    }

    /**
     * Функция перерисовки игрового поля. Для избежания мерцания изображение
     * сначала строится в буфере.
     */
    public void paint(Graphics _g)
    {
        if (buffer == null)
        {
            createBuffer();
        }

        gf.getField(cells);
        buffer.getGraphics().clearRect(0, 0, bufferX, bufferY);
        Graphics2D g = (Graphics2D) buffer.getGraphics();
        int i = 0, j = 0;
        int windowHeight = (this.getSize().height);
        int windowWidth = (this.getSize().width);
        int qolHor = cells[0].length;
        int qolVert = cells.length;
        cellSize = windowWidth / qolHor < windowHeight / qolVert ? windowWidth / qolHor : windowHeight / qolVert;

        // Если размер одной клетки получается меньше 4-х точек, то не уменьшать
        // его, а оставить равным 4.
        if (cellSize < 4)
        {
            cellSize = 4;
        }

        g.setColor(Color.gray);

        // Разлинуем поле:
        {
            // Вертикальные линии
            for (i = 0; i <= qolHor; i++)
            {
                g.drawLine(i * cellSize, 0, i * cellSize, qolVert * cellSize);
            }
            // Горизонтальные линии
            for (i = 0; i <= qolVert; i++)
            {
                g.drawLine(0, i * cellSize, qolHor * cellSize, i * cellSize);
            }
        }

        // В местах, где расположены клетки надо нарисовать кружочки:
        {
            g.setColor(Color.red);
            for (i = 0; i <= cells.length - 1; i++)
            {
                for (j = 0; j <= cells[i].length - 1; j++)
                {
                    if (cells[i][j] == true)
                    {
                        g.drawOval(
                                   j * cellSize + 1, i * cellSize + 1,
                                   cellSize - 2, cellSize - 2);
                    }
                }
            }
        }
        _g.drawImage(buffer, 0, 0, null);
    }

    /**
     * Установим интервал с которым надо выводить изображение на экран.
     * 
     * @param _pause
     *            миллисекунды
     */
    public void setTimer(int _pause)
    {
        if (_pause != intPauseSlow)
        {
            intUserPause = _pause;
        }

        intCurrentPause = _pause;

        System.out.println("Timer = new " + _pause);
        if (timer != null)
        {
            timer.cancel();
        }
        pause = false;
        // Запустить расчёт игрового поля.
        gf.start();
        // Вывод результатов расчёта по таймеру.
        timer = new java.util.Timer();
        timer.schedule(new java.util.TimerTask()
        {
            public void run()
            {
                if (!pause)
                {
                    paint(getGraphics());
                }
            }
        }, new Date(), intCurrentPause);
    }

}

class form extends Frame
{
    classPlayField f;
    Button btnClear, btnGo, btnStop;

    form()
    {
        f = new classPlayField(50, 50);
        this.setSize(300, 300);
        this.setVisible(true);
        this.setLayout(new BorderLayout());
        this.add(f, BorderLayout.CENTER);
        Container bl = new Container();
        // bl.setLayout(new BoxLayout(bl, BoxLayout.X_AXIS) );
        bl.setLayout(new FlowLayout(FlowLayout.CENTER));
        btnClear = new Button("Clear");
        btnGo = new Button("Go");
        btnStop = new Button("Stop");
        bl.add(btnClear);
        bl.add(btnGo);
        bl.add(btnStop);
        bl.setVisible(true);
        this.add(bl, BorderLayout.PAGE_END);

        // Обработать событие зазкрытия окна при Alt-А4 или нажатии крестика в
        // углу окна.
        this.addWindowListener(new WindowAdapter()
        {
            public void windowClosing(WindowEvent e)
            {
                System.exit(0);
            }
        });

        btnClear.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                f.pause = true;
                f.clear();
            }
        });
        btnGo.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                btnGo.setEnabled(false);
                btnStop.setEnabled(true);
                // f.pause=false;
                f.setTimer(10);
            }
        });
        btnStop.addActionListener(new ActionListener()
        {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                // f.pause=!f.pause;
                System.out.println("pause begin...");
                // *
                synchronized (f.gf)
                {
                    f.gf.pause = !f.gf.pause;
                    btnGo.setEnabled(true);
                    btnStop.setEnabled(false);
                }
                // */
            }
        });

    }
}

public class program
{

    /**
     * @param args
     */
    public static void main(String[] args)
    {
        new form();
    }

}
